/*************************************************************************
 * Copyright 2008 David Almilli
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *      
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 **************************************************************************/
package com.tivo.hme.bananas;

import com.tivo.core.ds.MapThreadWrapper;
import com.tivo.hme.interfaces.ILogger;
import com.tivo.hme.sdk.Resolution;
import com.tivo.hme.sdk.Resource;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

public class BSkinPlus extends BSkin {
	
	private Map<String, String> propertyMap;
	private Map<Object, WeakReference<Resource>> resourceMap;

	@SuppressWarnings("unchecked")
	public BSkinPlus(BApplicationPlus app) {
		super(app);

		synchronized (lock) {
            map = new MapThreadWrapper(new HashMap(), lock);
			propertyMap = new MapThreadWrapper(new HashMap<String, String>(), lock);
			resourceMap = new MapThreadWrapper(new HashMap<Object, WeakReference<Resource>>(), lock);
		}
	}
	
	protected Resource getCachedResource(Object resourceKey) {
		WeakReference<Resource> ref = resourceMap.get(resourceKey);
		if (ref != null) {
			return ref.get();
		} else {
			return null;
		}
	}
	
	protected void setCachedResource(Object resourceKey, Resource resource) {
		resourceMap.put(resourceKey, new WeakReference<Resource>(resource));
	}
	
	public void configure(Resolution res) {
		SkinUtils.init(this, res);
	}
	
	public String getProperty(String name) {
		return propertyMap.get(name);
	}

	public int getIntProperty(String name) {
		return getIntProperty(name, 0);
	}
	
	public int getIntProperty(String name, int defaultValue) {
		String str = propertyMap.get(name);
		if (str != null) {
			return Integer.parseInt(str);
		} else {
			return defaultValue;
		}
	}

	public boolean getBooleanProperty(String name) {
		return getBooleanProperty(name, false);
	}
	
	public boolean getBooleanProperty(String name, boolean defaultValue) {
		String str = propertyMap.get(name);
		if (str != null) {
			return str.equalsIgnoreCase("true");
		} else {
			return defaultValue;
		}
	}
	
	public void setProperty(String name, String value) {
		propertyMap.put(name, value);
	}
	
	public void setIntProperty(String name, int value) {
		setProperty(name, Integer.toString(value));
	}
	
	public void setBooleanProperty(String name, boolean value) {
		setProperty(name, value ? "true" : "false");
	}

	@Override
    public Element get(String name) {
        Element e = (Element)map.get(name);
        if (e == null) {
            if (map.isEmpty()) {
                //print warning message
                app.getLogger().log(ILogger.LOG_WARNING, "Using BSkinPlus before it has been properly " +
                        "configured.  You may experience unusual behavior in the user interface.");
            }
            return null;
        }
        
        //call get resource so we do any resource loading
        e.getResource();
        
        // stuff in a default asset if necessary
//        if (e.getResource() == null) {
//            e.setResource(app.getResource("com/tivo/hme/bananas/" + e.getName() + ".png"));
//        }
        
        return e;
    }

	public static class Element extends BSkin.Element {
		protected BSkinPlus skin;
		protected Object resourceKey;
		
        @SuppressWarnings("unchecked")
        protected Map properties;
        
        @SuppressWarnings("unchecked")
		private Map wrapper;

		public Element(BSkinPlus skin, String name, int width, int height, Resource resource) {
			super(skin, name, width, height, resource);
			this.skin = skin;
		}

		public Element(BSkinPlus skin, String name, int width, int height, Object resourceKey) {
			super(skin, name, width, height, null);
			this.resourceKey = resourceKey;
			this.skin = skin;
		}
		
		protected Resource getCachedResource(Object resourceKey) {
			return skin.getCachedResource(resourceKey);
		}
		
		protected void setCachedResource(Object resourceKey, Resource resource) {
			skin.setCachedResource(resourceKey, resource);
		}
		
		public Resource getResource() {
			Resource res = super.getResource();
			if (res == null) {
				res = createResource();
                if (res != null) {
                    setResource(res);
                }
			}
			return res;
		}

		public Object getResourceKey() {
			return resourceKey;
		}
		
		public Resource createResource() {
			synchronized (skin.app.getMonitor()) {
				if (resourceKey != null) {
					Resource resource = getCachedResource(resourceKey);
					if (resource == null) {
						resource = skin.app.getResource(resourceKey);
						setCachedResource(resourceKey, resource);
					}
					return resource;
				} else {
	                return null;
	            }
			}
		}

        @Override
        public String get(String key) {
            return get(key, null);
        }
        
        public String get(String key, String defaultValue) {
            Object value = getObject(key);
            if (value != null) {
                return value.toString();
            } else {
                return defaultValue;
            }
        }

        public boolean containsKey(String key) {
            if (properties == null) {
                return false;
            }
            return properties.containsKey(key);
        }
        
        public Object getObject(String key) {
            synchronized (lock) {
                if (properties == null) {
                    return null;
                }
                return properties.get(key);
            }
        }

        @Override
        public int getInt(String key, int defaultValue) {
            synchronized (lock) {
                String value = get(key);
                return (value != null) ? Integer.parseInt(value) : defaultValue;
            }
        }
        
        public int getInt(String name) {
            return getInt(name, 0);
        }
        
        public boolean getBoolean(String name, boolean defaultValue) {
            String str = get(name);
            if (str != null) {
                return str.equalsIgnoreCase("true");
            } else {
                return defaultValue;
            }
        }
        
        public boolean getBoolean(String name) {
            return getBoolean(name, false);
        }

        @Override
        public void set(String key, String value) {
            setObject(key, value);
        }

        @SuppressWarnings("unchecked")
        public void setObject(String key, Object value) {
            synchronized (lock) {
                if (properties == null) {
                    properties = new HashMap();
                }
                properties.put(key, value);
            }
        }
        
        public void setInt(String key, int value) {
            set(key, Integer.toString(value));
        }
        
        public void setBoolean(String key, boolean value) {
            set(key, value ? "true" : "false");
        }

		@Override
        @SuppressWarnings("unchecked")
        public Map getProperties() {
            synchronized (lock) {
                if (properties != null && wrapper == null) {
                    wrapper = new MapThreadWrapper(properties, lock);
                }
                return wrapper;
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void setProperties(Map properties) {
            synchronized (lock) {
                this.properties = properties;
                this.wrapper = null;
            }
        }

	}
}
